package com.acgist.snail.context;

import java.nio.channels.AsynchronousChannelGroup;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

import com.acgist.snail.logger.Logger;
import com.acgist.snail.logger.LoggerFactory;

/**
 * 系统线程上下文
 * 
 * @author acgist
 */
public final class SystemThreadContext implements IContext {

	private static final Logger LOGGER = LoggerFactory.getLogger(SystemThreadContext.class);
	
	/**
	 * 系统线程名称
	 * Snail-Thread
	 */
	public static final String SNAIL_THREAD = "ST";
	/**
	 * BT线程名称
	 */
	public static final String SNAIL_THREAD_BT = SNAIL_THREAD + "-BT";
	/**
	 * HLS线程名称
	 */
	public static final String SNAIL_THREAD_HLS = SNAIL_THREAD + "-HLS";
	/**
	 * Costed线程名称
	 */
	public static final String SNAIL_THREAD_COSTED = SNAIL_THREAD + "-Costed";
	/**
	 * 定时线程名称
	 */
	public static final String SNAIL_THREAD_SCHEDULED = SNAIL_THREAD + "-Scheduled";
	/**
	 * BT定时线程名称
	 */
	public static final String SNAIL_THREAD_BT_SCHEDULED = SNAIL_THREAD_BT + "-Scheduled";
	/**
	 * UTP队列线程名称
	 */
	public static final String SNAIL_THREAD_UTP_QUEUE = SNAIL_THREAD + "-UTP-Queue";
	/**
	 * UDP服务端线程名称
	 */
	public static final String SNAIL_THREAD_UDP_SERVER = SNAIL_THREAD + "-UDP-Server";
	/**
	 * TCP客户端线程名称
	 */
	public static final String SNAIL_THREAD_TCP_CLIENT = SNAIL_THREAD + "-TCP-Client";
	/**
	 * TCP服务端线程名称
	 */
	public static final String SNAIL_THREAD_TCP_SERVER = SNAIL_THREAD + "-TCP-Server";
	/**
	 * 下载器线程名称
	 */
	public static final String SNAIL_THREAD_DOWNLOADER = SNAIL_THREAD + "-Downloader";
	/**
	 * 系统线程池：异步执行、防止卡顿
	 */
	private static final ExecutorService EXECUTOR;
	/**
	 * 系统定时线程池：定时任务
	 */
	private static final ScheduledExecutorService EXECUTOR_SCHEDULED;
	/**
	 * 最大线程数量
	 */
	private static final int MAX_THREAD_INDEX = 99;
	/**
	 * 线程名称
	 */
	private static final String THREAD_NAME = "%s-%02d";
	/**
	 * 线程编号
	 */
	private static final Map<String, Integer> THREAD_INDEX = new HashMap<>();
	/**
	 * CPU核心数量
	 */
	public static final int DEFAULT_THREAD_SIZE = Runtime.getRuntime().availableProcessors();
	
	static {
		EXECUTOR = newExecutor(threadSize(4, 8), threadSize(16, 32), Short.MAX_VALUE, 60L, SNAIL_THREAD);
		EXECUTOR_SCHEDULED = newScheduledExecutor(threadSize(2, 4), SNAIL_THREAD_SCHEDULED);
		LOGGER.info("系统默认线程数量：{}", DEFAULT_THREAD_SIZE);
	}
	
	private SystemThreadContext() {
	}
	
	/**
	 * 计算线程数量
	 * 
	 * @param minSize 最小线程数量
	 * @param maxSize 最大线程数量
	 * 
	 * @return 线程数量
	 */
	public static final int threadSize(int minSize, int maxSize) {
		return
			DEFAULT_THREAD_SIZE < minSize ? minSize :
			DEFAULT_THREAD_SIZE > maxSize ? maxSize :
			DEFAULT_THREAD_SIZE;
	}
	
	/**
	 * 异步执行任务
	 * 
	 * @param runnable 任务
	 */
	public static final void submit(Runnable runnable) {
		EXECUTOR.submit(runnable);
	}

	/**
	 * 定时执行任务（单次执行）
	 * 
	 * @param delay 延迟时间
	 * @param unit 时间单位
	 * @param runnable 任务
	 * 
	 * @return 定时任务
	 */
	public static final ScheduledFuture<?> scheduled(long delay, TimeUnit unit, Runnable runnable) {
		ScheduledException.verify(delay);
		return EXECUTOR_SCHEDULED.schedule(runnable, delay, unit);
	}
	
	/**
	 * 定时执行任务（重复执行）
	 * 固定时间：周期不受执行时间影响
	 * 
	 * @param delay 延迟时间
	 * @param period 周期时间
	 * @param unit 时间单位
	 * @param runnable 任务
	 * 
	 * @return 定时任务
	 */
	public static final ScheduledFuture<?> scheduledAtFixedRate(long delay, long period, TimeUnit unit, Runnable runnable) {
		ScheduledException.verify(delay);
		ScheduledException.verify(period);
		return EXECUTOR_SCHEDULED.scheduleAtFixedRate(runnable, delay, period, unit);
	}
	
	/**
	 * 定时执行任务（重复执行）
	 * 固定周期：周期受到执行时间影响
	 * 
	 * @param delay 延迟时间
	 * @param period 周期时间
	 * @param unit 时间单位
	 * @param runnable 任务
	 * 
	 * @return 定时任务
	 */
	public static final ScheduledFuture<?> scheduledAtFixedDelay(long delay, long period, TimeUnit unit, Runnable runnable) {
		ScheduledException.verify(delay);
		ScheduledException.verify(period);
		return EXECUTOR_SCHEDULED.scheduleWithFixedDelay(runnable, delay, period, unit);
	}
	
	/**
	 * 新建固定线程池
	 * 
	 * @param minPoolSize 初始线程数量
	 * @param maxPoolSize 最大线程数量
	 * @param queueSize 等待线程队列长度
	 * @param keepAliveTime 线程空闲时间（秒）
	 * @param name 线程池名称
	 * 
	 * @return 固定线程池
	 */
	public static final ExecutorService newExecutor(int minPoolSize, int maxPoolSize, int queueSize, long keepAliveTime, String name) {
		return new ThreadPoolExecutor(
			minPoolSize,
			maxPoolSize,
			keepAliveTime,
			TimeUnit.SECONDS,
			new LinkedBlockingQueue<>(queueSize),
			newThreadFactory(name),
			(runnable, executor) -> LOGGER.warn("拒绝执行任务：{}-{}", runnable, executor)
		);
	}
	
	/**
	 * 新建缓存线程池
	 * 
	 * @param minPoolSize 初始线程数量
	 * @param keepAliveTime 线程空闲时间（秒）
	 * @param name 线程池名称
	 * 
	 * @return 缓存线程池
	 */
	public static final ExecutorService newCacheExecutor(int minPoolSize, long keepAliveTime, String name) {
		return new ThreadPoolExecutor(
			minPoolSize,
			Short.MAX_VALUE,
			keepAliveTime,
			TimeUnit.SECONDS,
			// 禁止添加队列
			new SynchronousQueue<>(),
			newThreadFactory(name),
			(runnable, executor) -> LOGGER.warn("拒绝执行任务：{}-{}", runnable, executor)
		);
	}
	
	/**
	 * 新建定时线程池
	 * 
	 * @param minPoolSize 初始线程数量
	 * @param name 线程池名称
	 * 
	 * @return 定时线程池
	 */
	public static final ScheduledExecutorService newScheduledExecutor(int minPoolSize, String name) {
		return new ScheduledThreadPoolExecutor(
			minPoolSize,
			newThreadFactory(name),
			(runnable, executor) -> LOGGER.warn("拒绝执行定时任务：{}-{}", runnable, executor)
		);
	}
	
	/**
	 * 新建线程池工厂
	 * 
	 * @param poolName 线程池名称
	 * 
	 * @return 线程池工厂
	 */
	private static final ThreadFactory newThreadFactory(String poolName) {
		return runnable -> {
			final Thread thread = new Thread(runnable);
			// 线程名称
			synchronized(THREAD_INDEX) {
				final int index = THREAD_INDEX.compute(poolName, (k, v) -> v == null || v >= MAX_THREAD_INDEX ? 1 : v + 1);
				thread.setName(String.format(THREAD_NAME, poolName, index));
			}
			// 守护线程
			thread.setDaemon(true);
			return thread;
		};
	}
	
	/**
	 * 关闭系统线程池
	 */
	public static final void shutdown() {
		LOGGER.debug("关闭系统线程池");
		shutdown(EXECUTOR);
		shutdown(EXECUTOR_SCHEDULED);
	}
	
	/**
	 * 关闭线程池
	 * 
	 * @param executor 线程池
	 */
	public static final void shutdown(ExecutorService executor) {
		shutdown(false, executor);
	}
	
	/**
	 * 关闭线程池（立即关闭）
	 * 
	 * @param executor 线程池
	 */
	public static final void shutdownNow(ExecutorService executor) {
		shutdown(true, executor);
	}
	
	/**
	 * 关闭线程池
	 * 正常关闭：不能继续添加任务，已经添加和正在执行的任务都会执行。
	 * 立即关闭：不能继续添加任务，不会执行排队任务，正在运行任务调用线程interrupt方法。
	 * 
	 * @param closeNow 是否立即关闭
	 * @param executor 线程池
	 */
	private static final void shutdown(boolean closeNow, ExecutorService executor) {
		if(executor == null || executor.isShutdown()) {
			return;
		}
		try {
			if(closeNow) {
				executor.shutdownNow();
			} else {
				executor.shutdown();
			}
		} catch (Exception e) {
			LOGGER.error("关闭线程池异常", e);
		}
	}
	
	/**
	 * 关闭定时任务
	 * 
	 * @param scheduledFuture 定时任务
	 */
	public static final void shutdown(ScheduledFuture<?> scheduledFuture) {
		shutdown(false, scheduledFuture);
	}
	
	/**
	 * 关闭定时任务（立即关闭）
	 * 
	 * @param scheduledFuture 定时任务
	 */
	public static final void shutdownNow(ScheduledFuture<?> scheduledFuture) {
		shutdown(true, scheduledFuture);
	}
	
	/**
	 * 关闭定时任务
	 * 正常关闭：正在运行的任务不会取消执行
	 * 立即关闭：正在运行的任务将被取消执行
	 * 
	 * @param closeNow 是否立即关闭
	 * @param scheduledFuture 定时任务
	 */
	private static final void shutdown(boolean closeNow, ScheduledFuture<?> scheduledFuture) {
		if(scheduledFuture == null || scheduledFuture.isCancelled()) {
			return;
		}
		try {
			scheduledFuture.cancel(closeNow);
		} catch (Exception e) {
			LOGGER.error("关闭定时任务异常", e);
		}
	}
	
	/**
	 * 关闭异步通道线程池
	 * 
	 * @param group 异步通道线程池
	 */
	public static final void shutdown(AsynchronousChannelGroup group) {
		shutdown(false, group);
	}
	
	/**
	 * 关闭异步通道线程池（立即关闭）
	 * 
	 * @param group 异步通道线程池
	 */
	public static final void shutdownNow(AsynchronousChannelGroup group) {
		shutdown(true, group);
	}
	
	/**
	 * 关闭异步通道线程池
	 * 
	 * @param closeNow 是否立即关闭
	 * @param group 异步通道线程池
	 */
	private static final void shutdown(boolean closeNow, AsynchronousChannelGroup group) {
		if(group == null || group.isShutdown()) {
			return;
		}
		try {
			if(closeNow) {
				group.shutdownNow();
			} else {
				group.shutdown();
			}
		} catch (Exception e) {
			LOGGER.error("关闭异步通道线程池异常", e);
		}
	}

}
